////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//                                                                                                                            //
//                                                         WNProject                                                          //
//                                                                                                                            //
//            This file is distributed under the BSD 2-Clause open source license. See License.txt for details.               //
//                                                                                                                            //
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#ifndef __WN_CONCURRENCY_BENAPHORE_INL__
#define __WN_CONCURRENCY_BENAPHORE_INL__

#include "WNCore/inc/WNPlatform.h"
#include "WNCore/inc/WNTypes.h"
#include "WNCore/inc/WNAssert.h"
#include "WNCore/inc/WNAtomic.h"

#ifdef _WN_WINDOWS
    #include <windows.h>
#elif defined _WN_LINUX
    #include <pthread.h>
#endif

namespace WNConcurrency {
    WN_INLINE WNBenaphore::WNBenaphore() :
        mCounter(0) {
        #ifdef _WN_WINDOWS
            mHandle = CreateSemaphore(WN_NULL, 0, 1, WN_NULL);

            WN_RELEASE_ASSERT(mHandle != WN_NULL);
        #elif defined _WN_LINUX
            pthread_mutexattr_t attributes;

            WN_RELEASE_ASSERT(pthread_mutexattr_init(&attributes) == 0);
            WN_RELEASE_ASSERT(pthread_mutexattr_settype(&attributes, PTHREAD_MUTEX_NORMAL) == 0);
            WN_RELEASE_ASSERT(pthread_mutexattr_setrobust(&attributes, PTHREAD_MUTEX_ROBUST) == 0);
            WN_RELEASE_ASSERT(pthread_mutex_init(&mMutex, &attributes) == 0);
            WN_RELEASE_ASSERT(pthread_mutexattr_destroy(&attributes) == 0);
        #endif
    }

    WN_INLINE WNBenaphore::~WNBenaphore() {
        #ifdef _WN_WINDOWS
            WN_RELEASE_ASSERT(CloseHandle(mHandle));
        #elif defined _WN_LINUX
            WN_RELEASE_ASSERT(pthread_mutex_destroy(&mMutex) == 0);
        #endif
    }

    WN_INLINE WN_VOID WNBenaphore::Lock() {
        if (WNAtomicIncrement(&mCounter) > 1) {
            #ifdef _WN_WINDOWS
                WN_RELEASE_ASSERT(WaitForSingleObject(mHandle, INFINITE) != WAIT_OBJECT_0);
            #elif defined _WN_LINUX
                WN_RELEASE_ASSERT(pthread_mutex_lock(&mMutex) == 0);
            #endif
        }
    }

    WN_INLINE WN_BOOL WNBenaphore::TryLock() {
        return(WNAtomicCompareSwap(&mCounter, 1, 0) != 0 ? WN_TRUE : WN_FALSE);
    }

    WN_INLINE WN_VOID WNBenaphore::Unlock() {
        if (WNAtomicDecrement(&mCounter) > 0) {
            #ifdef _WN_WINDOWS
                WN_RELEASE_ASSERT(ReleaseSemaphore(mHandle, 1, WN_NULL));
            #elif defined _WN_LINUX
                WN_RELEASE_ASSERT(pthread_mutex_unlock(&mMutex) == 0);
            #endif
        }
    }

    WN_INLINE WNBenaphoreScopeLock::WNBenaphoreScopeLock(WNBenaphore& _benaphore) :
        mLocked(WN_TRUE),
        mBenaphore(_benaphore) {
        mBenaphore.Lock();
    }

    WN_INLINE WNBenaphoreScopeLock::~WNBenaphoreScopeLock() {
        if (mLocked) {
            mBenaphore.Unlock();

            mLocked = WN_FALSE;
        }
    }
}

#endif // __WN_CONCURRENCY_BENAPHORE_INL__